
const { getAst, getDependecies, transform } = require("./parser");
const path = require("path");
const fs = require("fs");

module.exports = class Compiler {
  constructor(options) {
    // webpack 配置
    const { entry, output } = options
    // 入口
    this.entry = entry
    // 出口
    this.output = output
    // 模块
    this.modules = []
  }
  // 构建启动
  run() {
    // 解析入口文件
    const info = this.build(this.entry)
    this.modules.push(info)
    
    for (let i = 0; i < this.modules.length; i++) {
      const deps = this.modules[i].dependecies
      if (deps) {
        for (const dependency in deps) {
          // 列表循环所有的依赖
          this.modules.push(this.build(deps[dependency]))
        }
      }
    }

    

    // 生成依赖关系图
    // 重新整理this.modules 用filename当作key
    const dependencyGraph = this.modules.reduce((graph, item) => ({
      ...graph,
      // 使用文件路径作为每个模块的唯一标识符,保存对应模块的依赖对象和文件内容
      [item.filename]: {
        dependecies: item.dependecies,
        code: item.code
      }
    }), {})
    

    this.generate(dependencyGraph)
  }

  // 构建模块相关
  build(filename) {
    const ast = getAst(filename)
    const dependecies = getDependecies(ast, filename)
    const code = transform(ast)
    return {
      // 文件路径,可以作为每个模块的唯一标识符
      filename,
      // 依赖对象,保存filename目录文件中的所以依赖的模块路径
      dependecies,
      // filename文件内容
      code
    }
  }


  // 重写 require函数 (浏览器不能识别commonjs语法),输出bundle
  generate(dependencyGraph) {
    // 输出文件路径
    const filePath = path.join(this.output.path, this.output.filename)
    /**
     * 定义localRequire
     * 定义exports
     * 
     */
    const bundle = `(function(graph){
      function require(fileName){

        function localRequire(relativePath){
          return require(graph[fileName].dependecies[relativePath])
        }

         const module = { exports:{}};

        (function(require,module,exports,code){
          // code是每个文件的内容，文件中可能依赖require其他文件，这时候就要递归去收集各个文件的内容并且使用module.exports导出
          eval(code)
        })(localRequire,module,module.exports,graph[fileName].code);

        // eval中 module.exports 的内容会被保存到module中，所以只要导出module.exports就行
        return module.exports;
      }
      // 从入口文件开始
      require('${this.entry}')
    })(${JSON.stringify(dependencyGraph)})`

    // 把文件内容写入到文件系统
    fs.writeFileSync(filePath, bundle, 'utf-8')
  }
}


// this.modules
/** 
     *  
     * [
        {
          filename: './src/index.js',
          dependecies: { './aa.js': './src/aa.js', './hh.js': './src/hh.js' },
          code: '"use strict";\n' +
            '\n' +
            'var _aa = require("./aa.js");\n' +
            '\n' +
            'var _hh = require("./hh.js");\n' +
            '\n' +
            '// src/index\n' +
            'document.write("".concat(_aa.xrc).concat(_hh.name));'
        },
        {
          filename: './src/aa.js',
          dependecies: {},
          code: '"use strict";\n' +
            '\n' +
            'Object.defineProperty(exports, "__esModule", {\n' +
            '  value: true\n' +
            '});\n' +
            'exports.xrc = void 0;\n' +
            '// src/aa.js\n' +
            "var xrc = '大名鼎鼎';\n" +
            'exports.xrc = xrc;'
        },
        {
          filename: './src/hh.js',
          dependecies: {},
          code: '"use strict";\n' +
            '\n' +
            'Object.defineProperty(exports, "__esModule", {\n' +
            '  value: true\n' +
            '});\n' +
            'exports.name = void 0;\n' +
            '// src/hh.js\n' +
            "var name = '周毅';\n" +
            'exports.name = name;'
        }
      ]
     */


      // dependencyGraph
    /**
     * 
     * 
     * {
        './src/index.js': {
          dependecies: { './aa.js': './src/aa.js', './hh.js': './src/hh.js' },
          code: '"use strict";\n' +
            '\n' +
            'var _aa = require("./aa.js");\n' +
            '\n' +
            'var _hh = require("./hh.js");\n' +
            '\n' +
            '// src/index\n' +
            'document.write("".concat(_aa.xrc).concat(_hh.name));'
        },
        './src/aa.js': {
          dependecies: {},
          code: '"use strict";\n' +
            '\n' +
            'Object.defineProperty(exports, "__esModule", {\n' +
            '  value: true\n' +
            '});\n' +
            'exports.xrc = void 0;\n' +
            '// src/aa.js\n' +
            "var xrc = '大名鼎鼎';\n" +
            'exports.xrc = xrc;'
        },
        './src/hh.js': {
          dependecies: {},
          code: '"use strict";\n' +
            '\n' +
            'Object.defineProperty(exports, "__esModule", {\n' +
            '  value: true\n' +
            '});\n' +
            'exports.name = void 0;\n' +
            '// src/hh.js\n' +
            "var name = '周毅';\n" +
            'exports.name = name;'
        }
      }
     * 
     */